Activités (Activities)
Plus récente (Latest)
False
Image de fond de la bannière
Activités UIAutomation
Dernière mise à jour 3 avr. 2024

Injecter du code .NET

UiPath.Core.Activities.InjectDotNetCode

Description

Injecte le code .NET dans le thread d'interface utilisateur principal de l'application cible. Destiné à être utilisé avec les applications d'interface utilisateur .NET qui n'exposent pas les technologies d'automatisation traditionnelles ou ne peuvent pas être correctement ciblées par des moyens traditionnels.

Propriétés

Commun
  • Poursuite sur erreur (ContinueOnError) - Spécifie si l'automatisation doit continuer même si l'activité génère un message d'erreur. Ce champ prend uniquement en charge les valeurs booléennes (True, False). La valeur par défaut est False. Par conséquent, si le champ est vide et si une erreur est renvoyée, l'exécution du projet s'interrompt. Si la valeur est configurée sur True, l'exécution du projet continue indépendamment de toute erreur.

    Remarque : si cette activité est incluse dans l'activité Try Catch et que la valeur de la propriété ContinuerSurErreur est « True », aucune erreur ne sera détectée lors de l'exécution du projet.
  • NomAffichage (DisplayName) - Nom affiché de l'activité.
Entrée
  • Assembly : chemin d'accès complet à l'assembly .NET compilé que vous souhaitez injecter. Ce champ prend uniquement en charge les chaînes et les variables String.
  • Nom de la méthode : le nom de la méthode que vous voulez appeler. Seules les méthodes statiques publiques sont prises en charge.

    Remarque : un type ne peut pas contenir plusieurs méthodes portant le même nom. Si tel est le cas, une erreur est renvoyée au moment de l'exécution.
  • Target.ClippingRegion : définit le rectangle de détourage, en pixels, par rapport à l'objet UiElement, dans les sens suivants : gauche, haut, droite, bas. Elle prend en charge les nombres positifs et négatifs.
  • Élément cible (Target.Element) - Utiliser la variable UiElement renvoyée par une autre activité. Cette propriété ne peut pas être utilisée avec la propriété Selector. Ce champ prend en charge les variables UiElement uniquement.
  • Sélecteur de cible (Target.Selector) - Propriété de texte utilisée pour trouver un élément donné de l'interface utilisateur lors de l'exécution de l'activité. Il s'agit en fait d'un fragment XML spécifiant les attributs de l'élément d'interface graphique utilisateur que vous recherchez et de certains de ses parents.
  • Target.TimeoutMS - Spécifie la durée (en millisecondes) à attendre pour que l'activité s'exécute avant que l'erreur SelectorNotFoundException ne soit renvoyée. La valeur par défaut est de 30 000 millisecondes (30 secondes).
  • Attente cible prête (Target.WaitForReady) - Avant d'exécuter les actions, attendez que la cible soit prête. Les options suivantes sont disponibles :

    • None (Aucun) - Attend uniquement la création de l'élément de l'interface utilisateur cible avant d'exécuter l'action. Vous pouvez, par exemple, utiliser cette option si vous souhaitez récupérer uniquement du texte à partir d'une page Web ou cliquer sur un bouton spécifique sans être obligé d'attendre que tous les éléments de l'interface utilisateur soient chargés. Il est à noter que ceci peut avoir des conséquences indésirables si le bouton est basé sur des éléments qui ne sont pas encore chargés, comme par exemple des scripts.
    • Interactif/Terminé - Attend que tous les éléments d'IU de l'application cible soient créés avant d'exécuter l'action.

      Pour évaluer si une application est à l'état Interactif (Interactive) ou Terminé (Complete), les balises suivantes sont vérifiées :

    • Applications de bureau : un message wm_null est envoyé pour vérifier l'existence des balises <wnd>, <ctrl>, <java> ou <uia>. S'ils existent, l'activité est exécutée.
    • Applications Web (Web Applications)
    1. Internet Explorer : la balise <webctrl> est utilisée pour vérifier si l’état Prêt du document HTML est défini sur Terminé. En outre, l'état Occupé doit être défini sur « False ».
    2. Autres : la balise <webctrl> est utilisée pour vérifier si l’état Prêt du document HTML est défini sur Terminé.
    • Applications SAP (SAP applications) : une fois la présence de la balise <wnd> vérifiée, une API SAP spécifique est utilisée pour détecter si la session est occupée ou non.
  • NomType : le nom de la classe publique qui contient la méthode d'exécution. Ce champ prend uniquement en charge les chaînes et les variables String.
Divers
  • Privé (Private) - Si cette option est sélectionnée, les valeurs des variables et des arguments ne sont plus enregistrées au niveau Détaillé (Verbose).
Sortie
  • Résultat : le résultat de la méthode invoquée, stocké dans une variable Object. Ce champ prend uniquement en charge les variables Object.

Injecter du code dans des domaines d'application non par défaut

L'activité Injecter le code .NET ( Inject .NET Code) injecte le code uniquement dans l'élément AppDomain par défaut.

Certaines applications sont structurés de manière à ce que les éléments que vous devrez peut-être accéder ou manipuler ne sont pas situés dans l'AppDomain par défaut, de sorte qu'il ne suffit pas d'injecter du code dans l'AppDomain par défaut. Ainsi, si les éléments d'IU que vous souhaitez indiquer sont situés dans un autre AppDomain, ils ne sont pas disponibles pour le code injecté.

Pour résoudre ce problème, vous devez modifier le processus d'injection pour cibler l'AppDomain précis contenant les éléments de l'interface utilisateur. Plus exactement, vous devez injecter le code de la fonction exécutée dans l'AppDomain par défaut dans tous les AppDomains non-par défaut ou dans l'AppDomain spécifique non par défaut contenant les éléments d'IU que vous souhaitez indiquer.

Création d'un exécuteur multi-domaine d'application

Pour exécuter le code dans des domaines d'application non par défaut, vous devez créer une classe dérivée de MarshalByRefObject, contenant la fonction que vous souhaitez exécuter dans des domaines d'application non par défaut :
public class CrossAppDomainRunner : MarshalByRefObject
{
    public void Execute(IntPtr controlHandle)
    {
        // Implementation of the method that will be executed in the target AppDomain
        Trace.WriteLine("Sunt in " + AppDomain.CurrentDomain.FriendlyName);

        Control foundControl = null;
        try
        {
            foundControl = Control.FromHandle(controlHandle);
        }
        catch (Exception controlException)
        {
            int lastWin32Error = Marshal.GetLastWin32Error();
            Trace.WriteLine($"+++++ An exception occurred while getting the control from handle {controlHandle}. The message is: {controlException.Message}, Win32 Error: {lastWin32Error}");
            
        }
        if (foundControl != null)
        {
            Trace.WriteLine($"+++++ Found control {foundControl.Name} from handle {controlHandle}");
            
        }
        else
        {
            int lastWin32Error = Marshal.GetLastWin32Error();
            Trace.WriteLine($"+++++ Control NOT found based on handle {controlHandle}, Win32 Error: {lastWin32Error}");
            
        }
    }
}public class CrossAppDomainRunner : MarshalByRefObject
{
    public void Execute(IntPtr controlHandle)
    {
        // Implementation of the method that will be executed in the target AppDomain
        Trace.WriteLine("Sunt in " + AppDomain.CurrentDomain.FriendlyName);

        Control foundControl = null;
        try
        {
            foundControl = Control.FromHandle(controlHandle);
        }
        catch (Exception controlException)
        {
            int lastWin32Error = Marshal.GetLastWin32Error();
            Trace.WriteLine($"+++++ An exception occurred while getting the control from handle {controlHandle}. The message is: {controlException.Message}, Win32 Error: {lastWin32Error}");
            
        }
        if (foundControl != null)
        {
            Trace.WriteLine($"+++++ Found control {foundControl.Name} from handle {controlHandle}");
            
        }
        else
        {
            int lastWin32Error = Marshal.GetLastWin32Error();
            Trace.WriteLine($"+++++ Control NOT found based on handle {controlHandle}, Win32 Error: {lastWin32Error}");
            
        }
    }
}
Fondamentalement, vous avez une fonction Execute qui reçoit un paramètre IntPtr . Ce paramètre contient la valeur de la commande que vous essayez de trouver. Dans cet exemple, vous devez transmettre uniquement un paramètre IntPtr , mais dans votre situation particulière, vous pouvez ajouter autant de paramètres que vous le souhaitez.

Énumération et injectation du code dans des domaines d'application non par défaut

Pour énumérer les autres AppDomains, il vous faut un pointeur vers l'interface ICorRuntimeHost de mscoree. Pour ce faire, vous devez déclarer cette interface :
using System;
                using System.Collections.Generic;
                using System.Diagnostics;
                using System.Reflection;
                using System.Runtime.InteropServices;
                
                namespace mscoree
                {
                [CompilerGenerated]
                [Guid("CB2F6722-AB3A-11D2-9C40-00C04FA30A3E")]
                [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
                [TypeIdentifier]
                [ComImport]
                [CLSCompliant(false)]
                public interface ICorRuntimeHost
                {
                void _VtblGap1_11();
                
                void EnumDomains(out IntPtr enumHandle);
                
                void NextDomain([In] IntPtr enumHandle, [MarshalAs(UnmanagedType.IUnknown)] out object appDomain);
                
                void CloseEnum([In] IntPtr enumHandle);
                }
                }using System;
                using System.Collections.Generic;
                using System.Diagnostics;
                using System.Reflection;
                using System.Runtime.InteropServices;
                
                namespace mscoree
                {
                [CompilerGenerated]
                [Guid("CB2F6722-AB3A-11D2-9C40-00C04FA30A3E")]
                [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
                [TypeIdentifier]
                [ComImport]
                [CLSCompliant(false)]
                public interface ICorRuntimeHost
                {
                void _VtblGap1_11();
                
                void EnumDomains(out IntPtr enumHandle);
                
                void NextDomain([In] IntPtr enumHandle, [MarshalAs(UnmanagedType.IUnknown)] out object appDomain);
                
                void CloseEnum([In] IntPtr enumHandle);
                }
                } 

Vous devez déclarer une classe contenant des méthodes natives pour initialiser le modèle d'objet de composant (COM) :

public static class NativeMethods
                {
                
                [DllImport("ole32.dll")]
                public static extern int CoInitializeEx(IntPtr pvReserved, COINIT dwCoInit);
                
                public enum COINIT : uint
                {
                /// Initializes the thread for multi-threaded object concurrency.
                COINIT_MULTITHREADED = 0x0,
                /// Initializes the thread for apartment-threaded object concurrency. 
                COINIT_APARTMENTTHREADED = 0x2,
                // ...
                }
                }public static class NativeMethods
                {
                
                [DllImport("ole32.dll")]
                public static extern int CoInitializeEx(IntPtr pvReserved, COINIT dwCoInit);
                
                public enum COINIT : uint
                {
                /// Initializes the thread for multi-threaded object concurrency.
                COINIT_MULTITHREADED = 0x0,
                /// Initializes the thread for apartment-threaded object concurrency. 
                COINIT_APARTMENTTHREADED = 0x2,
                // ...
                }
                }   
Vous avez besoin d'une méthode pour obtenir un objet ICorRunttimeHost :
public static ICorRuntimeHost GetCorRuntimeHost()
                {
                return (ICorRuntimeHost)Activator.CreateInstance(Marshal.GetTypeFromCLSID(new Guid("CB2F6723-AB3A-11D2-9C40-00C04FA30A3E")));
                }public static ICorRuntimeHost GetCorRuntimeHost()
                {
                return (ICorRuntimeHost)Activator.CreateInstance(Marshal.GetTypeFromCLSID(new Guid("CB2F6723-AB3A-11D2-9C40-00C04FA30A3E")));
                }
Vous avez maintenant tout en place pour énumérer les autres domaines d’application (AppDomains) et y exécuter du code. La fonction GetControlData est exécutée par l'activité Injecter du code .NET (Inject .NET Code) dans l'élément AppDomain par défaut :
public static void GetControlData(Int64 controlHandle, out string response)
                {
                //initialising COM
                NativeMethods.CoInitializeEx(IntPtr.Zero, NativeMethods.COINIT.COINIT_MULTITHREADED);
                
                mscoree.ICorRuntimeHost host = null;
                try
                {
                host = (ICorRuntimeHost)Activator.CreateInstance(Marshal.GetTypeFromCLSID(new Guid("CB2F6723-AB3A-11D2-9C40-00C04FA30A3E")));
                }
                catch (COMException comEx)
                {
                Trace.WriteLine($"COMException: {comEx.Message}, HRESULT: {comEx.ErrorCode}");
                }
                catch (Exception ex)
                {
                // Handle other exceptions
                Trace.WriteLine($"Exception: {ex.Message}");
                }
                
                
                
                IntPtr enumHandle = IntPtr.Zero;
                
                try
                {
                //now that we have ICorRuntimeHost object we can use it to enumerate the other domains
                host.EnumDomains(out enumHandle);
                object domain = null;
                host.NextDomain(enumHandle, out domain);
                while (domain != null)
                {
                //for each appdomain obtained
                AppDomain appDomain = (AppDomain)domain;
                //appDomain.BaseDirectory; - you might want to copy your dll 
                //in the appDomain.BaseDirectory cause otherwise it might not find 
                //the assembly 
                
                ObjectHandle handle = appDomain.CreateInstance(
                typeof(CrossAppDomainRunner).Assembly.FullName,
                typeof(CrossAppDomainRunner).FullName);
                
                // Unwrap to get the actual object
                
                var runnerProxy = handle.Unwrap();
                
                // Use reflection to call the Execute method
                MethodInfo executeMethod = runnerProxy.GetType().GetMethod("Execute", new Type[] { typeof(IntPtr) });
                //pass parameters as new object[]
                executeMethod.Invoke(runnerProxy, new object[] { new IntPtr(controlHandle) });
                
                //go to next appdomain
                host.NextDomain(enumHandle, out domain);
                if (domain == null)
                break;
                
                }
                }
                finally
                {
                if (host != null)
                {
                if (enumHandle != IntPtr.Zero)
                {
                host.CloseEnum(enumHandle);
                }
                
                Marshal.ReleaseComObject(host);
                }
                
                }
                response = string.Empty;
                }public static void GetControlData(Int64 controlHandle, out string response)
                {
                //initialising COM
                NativeMethods.CoInitializeEx(IntPtr.Zero, NativeMethods.COINIT.COINIT_MULTITHREADED);
                
                mscoree.ICorRuntimeHost host = null;
                try
                {
                host = (ICorRuntimeHost)Activator.CreateInstance(Marshal.GetTypeFromCLSID(new Guid("CB2F6723-AB3A-11D2-9C40-00C04FA30A3E")));
                }
                catch (COMException comEx)
                {
                Trace.WriteLine($"COMException: {comEx.Message}, HRESULT: {comEx.ErrorCode}");
                }
                catch (Exception ex)
                {
                // Handle other exceptions
                Trace.WriteLine($"Exception: {ex.Message}");
                }
                
                
                
                IntPtr enumHandle = IntPtr.Zero;
                
                try
                {
                //now that we have ICorRuntimeHost object we can use it to enumerate the other domains
                host.EnumDomains(out enumHandle);
                object domain = null;
                host.NextDomain(enumHandle, out domain);
                while (domain != null)
                {
                //for each appdomain obtained
                AppDomain appDomain = (AppDomain)domain;
                //appDomain.BaseDirectory; - you might want to copy your dll 
                //in the appDomain.BaseDirectory cause otherwise it might not find 
                //the assembly 
                
                ObjectHandle handle = appDomain.CreateInstance(
                typeof(CrossAppDomainRunner).Assembly.FullName,
                typeof(CrossAppDomainRunner).FullName);
                
                // Unwrap to get the actual object
                
                var runnerProxy = handle.Unwrap();
                
                // Use reflection to call the Execute method
                MethodInfo executeMethod = runnerProxy.GetType().GetMethod("Execute", new Type[] { typeof(IntPtr) });
                //pass parameters as new object[]
                executeMethod.Invoke(runnerProxy, new object[] { new IntPtr(controlHandle) });
                
                //go to next appdomain
                host.NextDomain(enumHandle, out domain);
                if (domain == null)
                break;
                
                }
                }
                finally
                {
                if (host != null)
                {
                if (enumHandle != IntPtr.Zero)
                {
                host.CloseEnum(enumHandle);
                }
                
                Marshal.ReleaseComObject(host);
                }
                
                }
                response = string.Empty;
                }

L'exemple ci-dessus injecte le code dans tous les AppDomains. Une fois que vous avez trouvé l'AppDomain où se trouvent les éléments d'IU que vous souhaitez indiquer, vous pouvez énumérer tous les AppDomains, mais injecter le code uniquement dans l'AppDomain nécessaire.

Cette page vous a-t-elle été utile ?

Obtenez l'aide dont vous avez besoin
Formation RPA - Cours d'automatisation
Forum de la communauté UiPath
Logo Uipath blanc
Confiance et sécurité
© 2005-2024 UiPath. All rights reserved.